﻿/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.    
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
Portions copyright (c) 2012 Eric Lasota

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


using System;
using System.Collections.Generic;

using System.Text;

namespace MPEGnet
{
    partial class VidStream
    {
        short[] dct_storage = new short[64];
        /*
         *--------------------------------------------------------------
         *
         * ParseReconBlock --
         *
         *    Parse values for block structure from bitstream.
         *      n is an indication of the position of the block within
         *      the macroblock (i.e. 0-5) and indicates the type of 
         *      block (i.e. luminance or chrominance). Reconstructs
         *      coefficients from values parsed and puts in 
         *      block.dct_recon array in vid stream structure.
         *      sparseFlag is set when the block contains only one
         *      coeffictient and is used by the IDCT.
         *
         * Results:
         *    
         *
         * Side effects:
         *      Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */

        
        static int[] zigzag_direct =
        {
          0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12,
          19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
          42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
          58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
        };

        static uint nBitMask(int idx)
        {
            if (idx == 0)
                return 0;
            return 0xffffffff << (32 - idx);
        }

        static uint bitMask(int idx)
        {
            if (idx == 0)
                return 0xffffffff;
            return ~(0xffffffff << (32 - idx));
        }

        static uint rBitMask(int idx)
        {
            uint mask = 0xffffffff << idx;
            return mask;
        }

        static uint bitTest(int idx)
        {
            return (uint)(1) << (31 - idx);
        }

        void ParseReconBlock( int n )
        {
            int coeffCount=0;
  
            if (buf_length < 100)
                correct_underflow();

            int diff;
            int size, level=0, pos, coeff=0;
            uint run;
            byte[] iqmatrix;

            int qscale;

            short[] reconptr = dct_storage;
            Array.Clear(reconptr, 0, 64);

            if (mblock.mb_intra)
            {
                if (n < 4)
                {
                    /*
                     * Get the luminance bits.  This code has been hand optimized to
                     * get by the normal bit parsing routines.  We get some speedup
                     * by grabbing the next 16 bits and parsing things locally.
                     * Thus, calls are translated as:
                     *
                     *    show_bitsX  <-->   next16bits >> (16-X)
                     *    get_bitsX   <-->   val = next16bits >> (16-flushed-X);
                     *               flushed += X;
                     *               next16bits &= bitMask[flushed];
                     *    flush_bitsX <-->   flushed += X;
                     *               next16bits &= bitMask[flushed];
                     *
                     * I've streamlined the code a lot, so that we don't have to mask
                     * out the low order bits and a few of the extra adds are removed.
                     *    bsmith
                     */
                    uint next16bits, index;
                    int flushed;

                    next16bits = show_bits16();
                    index = next16bits >> (16-5);
                    if (index < 31)
                    {
                        size = (int)VlcTables.dct_dc_size_luminance[index].value;
                        flushed = VlcTables.dct_dc_size_luminance[index].num_bits;
                    }
                    else
                    {
                        index = next16bits >> (16-9);
                        index -= 0x1f0;
                        size = (int)VlcTables.dct_dc_size_luminance1[index].value;
                        flushed = VlcTables.dct_dc_size_luminance1[index].num_bits;
                    }
                    next16bits &= bitMask(16+flushed);

                    if (size != 0) {
                        flushed += size;
                        diff = (int)(next16bits >> (16-flushed));
                        if ((diff & bitTest(32-size)) == 0 )
                            diff = ((int)rBitMask(size) | (diff + 1));
                        diff <<= 3;
                    }
                    else
                        diff = 0;
                    flush_bits(flushed);
                    if ( (n == 0) && ((mblock.mb_address - mblock.past_intra_addr) > 1) )
                        coeff = diff + 1024;
                    else
                        coeff = diff + block.dct_dc_y_past;
                    block.dct_dc_y_past = (short)coeff;
                }
                else
                {
                    /* n = 4 or 5 */
                    /*
                     * Get the chrominance bits.  This code has been hand optimized to
                     * as described above
                     */

                    uint next16bits, index;
                    int flushed;

                    next16bits = show_bits16();
                    index = next16bits >> (16-5);
                    if (index < 31) {
                        size = (int)VlcTables.dct_dc_size_chrominance[index].value;
                        flushed = VlcTables.dct_dc_size_chrominance[index].num_bits;
                    }
                    else
                    {
                        index = next16bits >> (16-10);
                        index -= 0x3e0;
                        size = (int)VlcTables.dct_dc_size_chrominance1[index].value;
                        flushed = VlcTables.dct_dc_size_chrominance1[index].num_bits;
                    }
                    next16bits &= bitMask(16+flushed);
    
                    if (size != 0)
                    {
                            flushed += size;
                        diff = (int)(next16bits >> (16-flushed));
                        if ((diff & bitTest(32-size)) == 0)
                            diff = (int)rBitMask(size) | (diff + 1);
                        diff <<= 3;
                    }
                    else
                        diff = 0;
                    flush_bits(flushed);

                    /* We test 5 first; a result of the mixup of Cr and Cb */
                    coeff = diff;
                    if (n == 5)
                    {
                        if (mblock.mb_address - mblock.past_intra_addr > 1)
                            coeff += 1024;
                        else
                            coeff += block.dct_dc_cr_past;
                        block.dct_dc_cr_past = (short)coeff;
                    }
                    else
                    {
                        if (mblock.mb_address - mblock.past_intra_addr > 1)
                            coeff += 1024;
                        else
                            coeff += block.dct_dc_cb_past;
                        block.dct_dc_cb_past = (short)coeff;
                    }
                }

                reconptr[0] = (short)coeff;

                pos = 0;
                coeffCount = ((coeff != 0) ? 1 : 0);

                int i = 0; 

                if (picture.code_type != 4)
                {
                    qscale = (int)slice.quant_scale;
                    iqmatrix = intra_quant_matrix;
    
                    while(true)
                    {
                        DecodeDCTCoeffNext(out run, ref level);

	                    if (run >= VlcTables.END_OF_BLOCK)
                            break;

                        i = i + (int)run + 1;

                        pos = zigzag_direct[i&0x3f];

                        /* quantizes and oddifies each coefficient */
                        if (level < 0)
                        {
                            coeff = ((level<<1) * qscale * 
                                ((int) (iqmatrix[pos]))) / 16; 
                            coeff += (1 - (coeff & 1));
                        }
                        else
                        {
                            coeff = ((level<<1) * qscale * 
                                ((int) (iqmatrix[pos]))) >> 4; 
                            coeff -= (1 - (coeff & 1));
                        }

                        reconptr[pos] = (short)coeff;
                        coeffCount++;
                    }

                    flush_bits(2);
	                goto end;
                }
            }
            else
            {
                /* non-intra-coded macroblock */

                byte[] niqmatrix = non_intra_quant_matrix;
                qscale = (int)slice.quant_scale;

                DecodeDCTCoeffFirst(out run, ref level);

                int i = (int)run;

                pos = zigzag_direct[i&0x3f];

                /* quantizes and oddifies each coefficient */
                if (level < 0)
                {
                    coeff = (((level<<1) - 1) * qscale * 
                        ((int) (niqmatrix[pos]))) / 16; 
	                if ((coeff & 1) == 0) {coeff = coeff + 1;}
                }
                else
                {
                    coeff = (((level<<1) + 1) * qscale * 
                        ((int) (niqmatrix[pos]))) >> 4;
	                coeff = (coeff-1) | 1; /* equivalent to: if ((coeff&1)==0) coeff = coeff - 1; */
                }

                reconptr[pos] = (short)coeff;
                if (coeff != 0)
                    coeffCount = 1;

                if (picture.code_type != 4)
                {
                    while(true)
                    {
                        DecodeDCTCoeffNext(out run, ref level);

                        if (run >= VlcTables.END_OF_BLOCK)
	       	                break;

                        i = i+(int)run+1;

                        pos = zigzag_direct[i&0x3f];

                        if (level < 0)
                        {
                            coeff = (((level<<1) - 1) * qscale * 
                                ((int) (niqmatrix[pos]))) / 16; 
                            if ((coeff & 1) == 0)
                                coeff = coeff + 1;
                        }
                        else
                        {
                            coeff = (((level<<1) + 1) * qscale * 
                                ((int) (niqmatrix[pos]))) >> 4; 
                            coeff = (coeff-1) | 1; /* equivalent to: if ((coeff&1)==0) coeff = coeff - 1; */
                        }
                        reconptr[pos] = (short)coeff;
                        coeffCount++;
                    } /* end while */
                    flush_bits(2);
                    goto end;
                } /* end if (vid_stream->picture.code_type != 4) */
            }
            end:

            if( _skipFrame == 0 || (picture.code_type != B_TYPE) )
            {
                if (coeffCount == 1)
                {
                    //using (PDB.Trace("j_rev_dct_sparse"))
                    {
                        _recon.Blocks[n + mblock.mb_address * 6].SetDct(new DCTBLOCK(reconptr), true, pos);
                    }
                }
                else
                {
                    //using (PDB.Trace("j_rev_dct"))
                    {
                        _recon.Blocks[n + mblock.mb_address * 6].SetDct(new DCTBLOCK(reconptr), false, 0);
                    }
                }
            }
        }
    }
}
